package cn.xfdtool.slowsql.interceptor;

import cn.xfdtool.slowsql.dto.SlowSQLDTO;
import cn.xfdtool.slowsql.processor.SlowSQLProcessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.util.Properties;

/**
 * 慢SQL拦截器, 一般应用于线上问题监控
 *
 * @author xfd
 * 2022/5/14
 */
@Slf4j
@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class SlowSQLInterceptor implements Interceptor {

    /** 慢SQL 信息处理器 */
    private SlowSQLProcessor slowSQLProcessor;

    public SlowSQLInterceptor(SlowSQLProcessor slowSQLProcessor) {
        this.slowSQLProcessor = slowSQLProcessor;
    }
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();
        Object result = null;
        long start = System.currentTimeMillis();
        long end = -1L;
        Throwable curException = null;
        try {
            result = invocation.proceed();
            end = System.currentTimeMillis();
        } catch (Throwable e) {
            log.error("sql error.", e);
            curException = e;
            end = System.currentTimeMillis();
        }

        // 数据验证, 是否判断
        SlowSQLDTO slowSQLDTO = SlowSQLDTO.builder()
                                    .target(target)
                                    .invocation(invocation)
                                    .start(start)
                                    .end(end)
                                    .throwable(curException)
                                    .result(result)
                                    .build();
        this.slowSQLProcessor.addSlowSQLDTO(slowSQLDTO);

        // 异常直接抛出
        if (curException != null) {
            throw curException;
        }
        return result;
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {

    }
}
